﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using PloobsEngine.Utils;
using PloobsEngine.Modelo;
using PloobsEngine;
using ModelXML;

namespace Etapa1.Import
{
    public class AnimatedModel : IModelo
    {
        public void Serialize(System.Xml.XmlTextWriter textWriter)
        {
        }
        public void DeSerialize(System.Xml.XmlNode node,EngineStuff engine)
        {
        }        

        private const string CUSTOM = "CUSTOM";
        private String _name;
        private string _glowName = null;
        private string _bumpName = null;
        private string _specularName = null;
        private string _paralaxName = null;
        String textureName = null;
        Texture2D texture = null;
        Texture2D bump = null;
        Texture2D specular = null;
        Texture2D glow = null;
        Texture2D paralax = null;        
        BatchInformation[][] batchInformationS = null;
        VertexBuffer[] vertexBufferS = null;
        IndexBuffer[] indexBufferS = null;
        Matrix[] transformation = new Matrix[1];
        BoundingSphere boundingSphere;
        private String texturesPath = "Textures//";
        Etapa1.Import.Anim.Bone root;        
        
        /// <summary>
        /// Serialization 
        /// </summary>
        internal AnimatedModel()
        {
        }

        public AnimatedModel(Modelo modelo , Etapa1.Import.Anim.Bone root)
        {
            this.root = root;
            InitModel(modelo);
        }

        private VertexPositionNormalTextureIndiceWeight AddBoneInfoToVertex(VertexPositionNormalTextureIndiceWeight vertice, int index, float weight)
        {
            Vector4 oldi = vertice.BlendIndices;
            Vector4 oldw = vertice.BlendWeight;
            if (vertice.BlendIndices.X == -1)
            {
                oldi.X = index;
                vertice.BlendIndices = oldi;
                oldw.X = weight;
                vertice.BlendWeight = oldw;
            }
            else if (vertice.BlendIndices.Y == -1)
            {
                oldi.Y = index;
                vertice.BlendIndices = oldi;
                oldw.Y = weight;
                vertice.BlendWeight = oldw;
            }
            else if (vertice.BlendIndices.Z == -1)
            {
                oldi.Z = index;
                vertice.BlendIndices = oldi;
                oldw.Z = weight;
                vertice.BlendWeight = oldw;
            }
            else if (vertice.BlendIndices.W == -1)
            {
                oldi.W = index;
                vertice.BlendIndices = oldi;
                oldw.W = weight;
                vertice.BlendWeight = oldw;
            }
            else            
            {
                ///IMPOSSIBLE SIR
            }
         
            return vertice;
        }
        
        private void InitModel(Modelo modelo)
        {            
            Dictionary<String,Etapa1.Import.Anim.Bone> mapping = new Dictionary<string,Etapa1.Import.Anim.Bone>();
            Etapa1.Import.Anim.Bone.GetNameBoneMapping(root,ref mapping);            
            VertexPositionNormalTextureIndiceWeight[] vertices = new VertexPositionNormalTextureIndiceWeight[modelo.Vertices.Length];
            List<Vector3> points = new List<Vector3>();
            for (int i = 0; i < modelo.Vertices.Length; i++)
            {
                Vector3 position = new Vector3(modelo.Vertices[i].Position.x, modelo.Vertices[i].Position.y, modelo.Vertices[i].Position.z);
                points.Add(position);
                Vector3 normal = new Vector3(modelo.Vertices[i].Normal.x, modelo.Vertices[i].Normal.y, modelo.Vertices[i].Normal.z);
                normal.Normalize();
                Vector2 texCoord = new Vector2(modelo.Vertices[i].TextureCoord.x, modelo.Vertices[i].TextureCoord.y);
                Vector4 neg = Vector4.One * -1;
                vertices[i] = new VertexPositionNormalTextureIndiceWeight(position, normal, texCoord, Vector4.Zero, neg);
            }

            foreach (var item in modelo.SkinInfo.Bones)
            {
                for (int i = 0; i < item.SkinIndices.Count; i++)
                {
                    int indice = item.SkinIndices[i];
                    float weight = item.SkinWeights[i];
                    vertices[indice] = AddBoneInfoToVertex(vertices[indice], mapping[item.BoneName].Index, weight);                    
                }
            }

            Vector3 translation = ImporterHelper.toVector3(modelo.Transformation.translation);
            Vector3 scale = ImporterHelper.toVector3(modelo.Transformation.Scale);
            Matrix rotation = Matrix.CreateFromQuaternion(ImporterHelper.toQuaternion(modelo.Transformation.rotationQuaternion));
            transformation[0] = Matrix.CreateTranslation(translation) * rotation * Matrix.CreateScale(scale);

            vertexBufferS = new VertexBuffer[1];
            vertexBufferS[0] = new VertexBuffer(EngineStuff.GraphicsDevice, VertexPositionNormalTextureIndiceWeight.SizeInBytes * modelo.Vertices.Length, BufferUsage.None);
            vertexBufferS[0].SetData<VertexPositionNormalTextureIndiceWeight>(vertices);

            indexBufferS = new IndexBuffer[1];
            indexBufferS[0] = new IndexBuffer(EngineStuff.GraphicsDevice, typeof(int), modelo.Indices.Length, BufferUsage.None);
            indexBufferS[0].SetData(modelo.Indices);

            VertexDeclaration vd = new VertexDeclaration(EngineStuff.GraphicsDevice, VertexPositionNormalTextureIndiceWeight.VertexElements);

            batchInformationS = new BatchInformation[1][];
            batchInformationS[0] = new BatchInformation[1];
            batchInformationS[0][0] = new BatchInformation(0, vertices.Length, modelo.Indices.Length / 3, 0, 0, vd, vd.GetVertexStrideSize(0));

            for (int i = 0; i < points.Count; i++)
            {
                points[i] = Vector3.Transform(points[i], transformation[0]);
            }
            boundingSphere = BoundingSphere.CreateFromPoints(points);
            points = null;

            foreach (var item in modelo.Maps)
            {
                String path = item.Path;
                if (path.Contains("."))
                {
                    path = path.Split('.')[0];
                }
                switch (item.MapType)
                {
                    case MapTypes.Ambient:
                        break;
                    case MapTypes.Bump:
                        BumpMapName = "Textures//" + path;
                        break;
                    case MapTypes.Diffuse:
                        DiffuseMapName = "Textures//" + path;
                        break;
                    case MapTypes.Displacement:
                        break;
                    case MapTypes.Filter:
                        break;
                    case MapTypes.Glossiness:
                        break;
                    case MapTypes.Opacity:
                        break;
                    case MapTypes.Reflection:
                        break;
                    case MapTypes.Refraction:
                        break;
                    case MapTypes.SelfIllumination:
                        GlowName = "Textures//" + path;
                        break;
                    case MapTypes.Specular:
                        SpecularMapName = "Textures//" + path;
                        break;
                    case MapTypes.SpecularLevel:
                        break;
                    case MapTypes.Unknown:
                        break;
                    default:
                        break;
                }
            } 
        }

         
        public void LoadModelo()
        {            
            if (!String.IsNullOrEmpty(textureName) && textureName != CUSTOM) 
            {
                this.texture = EngineStuff.CustomContentManager.GetAsset<Texture2D>(textureName);
            }
            if (!String.IsNullOrEmpty(_bumpName) && _bumpName != CUSTOM)
            {
                this.bump = EngineStuff.CustomContentManager .GetAsset<Texture2D>(_bumpName);
            }
            if (!String.IsNullOrEmpty(_specularName) && _specularName != CUSTOM)
            {
                this.specular = EngineStuff.CustomContentManager.GetAsset<Texture2D>(_specularName);
            }
            if (!String.IsNullOrEmpty(_glowName) && _glowName != CUSTOM)
            {
                this.glow = EngineStuff.CustomContentManager.GetAsset<Texture2D>(_glowName);
            }
            if (!String.IsNullOrEmpty(_paralaxName) && _paralaxName != CUSTOM)
            {
                this.paralax= EngineStuff.CustomContentManager.GetAsset<Texture2D>(_paralaxName);
            }            
        }

        public string ParalaxName
        {
            get { return _paralaxName; }
            set { _paralaxName = value; }
        }

        public string GlowName
        {
            get { return _glowName; }
            set { _glowName = value; }
        }

        public string BumpMapName
        {
            get { return _bumpName; }
            set { _bumpName = value; }
        }

        public string SpecularMapName
        {
            get { return _specularName; }
            set { _specularName = value; }
        }

        public String DiffuseMapName
        {
            get { return textureName; }
            set { textureName = value; }
        }

        public String TexturesPath
        {
            get { return texturesPath; }
            set { texturesPath = value; }
        }



        #region IModelo Members

        public string Name
        {
            get { return this._name; }
            set { this._name = value; }
        }

        #endregion

        #region IModelo Members

        
        public void SetTexture(String textureName ,TextureType type)
        {
            switch (type)
	        {
		        case TextureType.DIFFUSE:
                  this.textureName = textureName;
                 break;
                case TextureType.SPECULAR:
                 this._specularName = textureName;
                 break;
                case TextureType.BUMP:
                 this._bumpName = textureName;
                 break;
                case TextureType.GLOW:
                 this._specularName = textureName;
                 break;
                case TextureType.PARALAX:
                 this._paralaxName = textureName;
                 break;
                default:
                 throw new Exception("Invalid Buffer");                 
	        }

            LoadModelo();
        }

        public void SetTexture(Texture2D tex, TextureType type)
        {
            switch (type)
            {
                case TextureType.DIFFUSE:
                    this.textureName = "CUSTOM";
                    this.texture = tex;
                    break;
                case TextureType.SPECULAR:
                    this.textureName = "CUSTOM";
                    this.specular = tex;                    
                    break;
                case TextureType.BUMP:
                    this.textureName = "CUSTOM";
                    this.bump = tex;
                    break;                
                case TextureType.GLOW:
                    this._glowName = "CUSTOM";
                    this.glow = tex;
                    break;
                case TextureType.PARALAX:
                    this._paralaxName= "CUSTOM";
                    this.paralax = tex;
                    break;
                default:
                    throw new Exception("Invalid operation");
            }            
        }

        #endregion

        #region IModelo Members


        public Matrix[] getBonesTransformation()
        {
            return transformation;
        }

        #endregion

        #region IModelo Members


        public object getModelTag()
        {
            return null;
        }

        #endregion

        #region IModelo Members
        
        /// <summary>
        /// Return null if the dont have this kind of texture
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public Texture2D getTexture(TextureType type)
        {
            if (type == TextureType.DIFFUSE)
            {
                return this.texture;
            }
            else if(type == TextureType.BUMP)
            {                
                return this.bump;
            }
            else if (type == TextureType.SPECULAR)
            {
                return this.specular;
            }
            else if (type == TextureType.GLOW)
            {
                return this.glow;
            }
            else if (type == TextureType.PARALAX)
            {
                return this.paralax;
            }
            else
            {
                return null;
            }
        }

        #endregion

        #region IModelo Members

        public BoundingSphere GetBoudingSphere()
        {
            return boundingSphere;
        }        

        public VertexBuffer GetVertexBuffer(int meshNumber)
        {
            return vertexBufferS[meshNumber];
        }

        public IndexBuffer GetIndexBuffer(int meshNumber)
        {
            return indexBufferS[meshNumber];
        }

        public BatchInformation[] GetBatchInformation(int meshNumber)
        {
            return batchInformationS[meshNumber];
        }

        #endregion

        #region IModelo Members


        public int GetParentBoneIndex(int meshNumber)
        {
            return 0;
        }

        #endregion

        #region IModelo Members

        public int MeshNumber
        {
            get { return 1; }
        }

        #endregion
    }
}
